/**
 *
 */
package com.angel.io.processors.runners.imports.impl;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.angel.common.helpers.StringHelper;
import com.angel.io.descriptor.FileProcessorDescriptor;
import com.angel.io.exceptions.InvalidRowDataException;
import com.angel.io.log.ProcessorLog;
import com.angel.io.parameters.ParametersService;
import com.angel.io.processors.commands.imports.ImportFileProcessorCommand;
import com.angel.io.processors.commands.imports.ImportRowProcessorCommand;
import com.angel.io.processors.runners.imports.ImportProcessorRunner;
import com.angel.io.type.cols.HeaderColumnFile;
import com.angel.io.type.rows.ImportRowFile;
import com.angel.io.type.rows.impl.HeaderRowFile;

/**
 *	@author William
 *	@since 15/January/2009.
 *
 */
public class ImportFileProcessorRunner implements ImportProcessorRunner{

	private static final Logger LOGGER = Logger.getLogger(ImportFileProcessorRunner.class);

	private FileProcessorDescriptor fileProcessorDescriptor;
	private HeaderRowFile headerRow;
	private ParametersService parametersService;
	private List<HeaderColumnFile> temporaryHeaderColumns = new ArrayList<HeaderColumnFile>();;
	private ImportFileProcessorCommand importFileCommand;
	private ImportRowProcessorCommand importRowCommand;

	public ImportFileProcessorRunner(FileProcessorDescriptor fileProcessorDescriptor, ImportFileProcessorCommand importFileCommand, ImportRowProcessorCommand importRowCommand){
		super();
		//TODO Verify if necessary this file processor descriptor.
		this.fileProcessorDescriptor = fileProcessorDescriptor;
		this.headerRow = new HeaderRowFile();
		this.parametersService = new ParametersService();
		this.importFileCommand = importFileCommand;
		this.importRowCommand = importRowCommand;
	}

	protected ImportFileProcessorRunner(FileProcessorDescriptor fileProcessorDescriptor, ImportFileProcessorCommand importFileCommand){
		super();
		//TODO Verify if necessary this file processor descriptor.
		this.fileProcessorDescriptor = fileProcessorDescriptor;
		this.headerRow = new HeaderRowFile();
		this.parametersService = new ParametersService();
		this.importFileCommand = importFileCommand;
	}

	/** Process each row from file.
	 *
	 * @param rowFiles to be processed.
	 * @return a list's object created at the process.
	 * @throws InvalidRowDataException when a invalid row data is found.
	 */
	protected <T> List<T> processAllRowFilesFromFile(List<ImportRowFile> rowFiles) {
		ProcessorLog processorLog = this.fileProcessorDescriptor.getProcessorLog();

		List<T> objects = new ArrayList<T>();
		if(rowFiles != null && rowFiles.size() > 0){
			long i = 0;
			ImportRowFile headerRowFile = null;
			try {
				headerRowFile = rowFiles.get(0);
				LOGGER.info("Adding alias to HEADER columns.");
				this.updateHeaderRow(headerRowFile);

				LOGGER.info("Processing HEADER row file number [" + i + "] with data [" + this.headerRow.toString() + "].");
				String[] headerColumnsNames = this.importRowCommand.getHeaderColumnsNames();

				boolean hasAllColumns = this.headerRow.hasAllColumnName(headerColumnsNames);

				if(!hasAllColumns){
					throw new InvalidRowDataException("Header is invalid. " + this.importRowCommand.getClass().getSimpleName() + " expects has [" + StringHelper.convertToPlainString(headerColumnsNames) + "].");
				}
				rowFiles.remove(0);
			} catch(InvalidRowDataException e){
				processorLog.addFailRowMessageLog(Long.valueOf(i), e.getMessage());
				LOGGER.error("Invalid HEADER data [" + headerRowFile.toString() + "] at row number [" + i + "] caused by: " + e.getMessage());
			}
			i++;
			for(ImportRowFile rowFile: rowFiles){
				try {
					LOGGER.info("Checking DATA for row file number [" + i + "] with data [" + rowFile.toString() + "].");
					this.importRowCommand.checkRowData(rowFile, this.headerRow);

					LOGGER.info("Processing DATA for row file number [" + i + "] with data [" + rowFile.toString() + "].");
					T obj = this.importRowCommand.processRow(rowFile, this.headerRow, this.parametersService);
					objects.add(obj);
					processorLog.addProcessedRowMessageLog(Long.valueOf(i), "OK");
					i++;
				} catch(InvalidRowDataException e){
					processorLog.addFailRowMessageLog(Long.valueOf(i), e.getMessage());
					LOGGER.error("Invalid data [" + rowFile.toString() + "] at row number [" + i + "] caused by: " + e.getMessage());
				}
			}
		}
		return objects;
	}

	/* (non-Javadoc)
	 * @see com.angel.io.processors.runner.interfaces.ProcessorRunner#runProcess(java.io.InputStream)
	 */
	public <T extends Object> List<T> runProcess(InputStream inputStream, Integer... pages) throws InvalidRowDataException {
		LOGGER.info("Begin run process.");

		LOGGER.info("Initialize file processor...");
		this.importFileCommand.initialize(inputStream);

		List<T> allObjects = new ArrayList<T>();
		if(pages != null && pages.length > 0){
			for(Integer numberPage: pages){
				this.clearHeaderRowFile();
				LOGGER.info("Begin processing page [" + numberPage + "] with file processor...");
				List<ImportRowFile> rowFiles = this.importFileCommand.processFile(numberPage);
				LOGGER.info("Processed [" + rowFiles.size() + "] row files for page number [" + numberPage + "].");
				List<T> objects = this.processAllRowFilesFromFile(rowFiles);
				LOGGER.info("Processed [" + objects.size() + "] entities for page number [" + numberPage + "].");
				allObjects.addAll(objects);
				LOGGER.info("Total processed [" + allObjects.size() + "] entities.");
			}
		} else {
			LOGGER.info("Begin processing page with file processor...");

			List<ImportRowFile> rowFiles = this.importFileCommand.processFile(new Integer(0));
			LOGGER.info("Processed [" + rowFiles.size() + "] row files for page number [" + "0" + "].");
			List<T> objects = this.processAllRowFilesFromFile(rowFiles);
			LOGGER.info("Processed [" + objects.size() + "] entities for page number [" + "0" + "].");
			allObjects.addAll(objects);
			LOGGER.info("Total processed [" + allObjects.size() + "] entities.");
		}

		LOGGER.info("Finish file processor...");
		this.importFileCommand.finish();
		return allObjects;

	}

	protected void clearHeaderRowFile() {
		this.headerRow.removeColumns();
	}

	protected void updateHeaderRow(ImportRowFile rowFile) {
		/** Add columns to header row. */
		for(int i = 0; i < rowFile.quantityColumns(); i++){
			this.headerRow.addColumn(i, rowFile.getStringValue(i));
		}
		/** Update columns alias. */
		for(HeaderColumnFile col: this.temporaryHeaderColumns){
			this.headerRow.addAllAliasFor(col.getColumnName(), col.getAlias());
		}
	}

	public void addAliasFor(String columnName, String aliasName) {
		HeaderColumnFile col = this.findHeaderColumnFile(columnName);
		col.addAliasName(aliasName);
	}

	private HeaderColumnFile findHeaderColumnFile(String columnName){
		for(HeaderColumnFile col: this.temporaryHeaderColumns){
			if(col.getColumnName().equalsIgnoreCase(columnName)){
				return col;
			}
		}
		HeaderColumnFile newCol = new HeaderColumnFile(columnName);
		this.temporaryHeaderColumns.add(newCol);
		return newCol;
	}


	public void addAliasFor(String columnName, List<String> aliasNames) {
		for(String alias: aliasNames){
			this.addAliasFor(columnName, alias);
		}
	}

	public String findParameter(Object parameter){
		return this.parametersService.findParameter(parameter);
	}

	public Object findParameter(String name){
		return this.parametersService.findParameter(name);
	}

	public void removeParameter(String name){
		this.parametersService.removeParameter(name);
	}

	public boolean containsParameter(Object parameter){
		return this.parametersService.containsParameter(parameter);
	}

	public boolean containsParameter(String name){
		return this.parametersService.containsParameter(name);
	}

	public void addParameter(String name, Object parameter){
		this.parametersService.addParameter(name, parameter);
	}

	/**
	 * @return the importFileCommand
	 */
	public ImportFileProcessorCommand getImportFileCommand() {
		return importFileCommand;
	}

	/**
	 * @return the importRowCommand
	 */
	public Object getImportRowCommand() {
		return importRowCommand;
	}

	/**
	 * @return the fileProcessorDescriptor
	 */
	protected FileProcessorDescriptor getFileProcessorDescriptor() {
		return fileProcessorDescriptor;
	}

	/**
	 * @return the headerRow
	 */
	protected HeaderRowFile getHeaderRow() {
		return headerRow;
	}

	/**
	 * @return the parametersService
	 */
	protected ParametersService getParametersService() {
		return parametersService;
	}

	/**
	 * @return the temporaryHeaderColumns
	 */
	protected List<HeaderColumnFile> getTemporaryHeaderColumns() {
		return temporaryHeaderColumns;
	}

	/**
	 * @param fileProcessorDescriptor the fileProcessorDescriptor to set
	 */
	protected void setFileProcessorDescriptor(
			FileProcessorDescriptor fileProcessorDescriptor) {
		this.fileProcessorDescriptor = fileProcessorDescriptor;
	}

	/**
	 * @param headerRow the headerRow to set
	 */
	protected void setHeaderRow(HeaderRowFile headerRow) {
		this.headerRow = headerRow;
	}

	/**
	 * @param parametersService the parametersService to set
	 */
	protected void setParametersService(ParametersService parametersService) {
		this.parametersService = parametersService;
	}

	/**
	 * @param temporaryHeaderColumns the temporaryHeaderColumns to set
	 */
	protected void setTemporaryHeaderColumns(
			List<HeaderColumnFile> temporaryHeaderColumns) {
		this.temporaryHeaderColumns = temporaryHeaderColumns;
	}

	/**
	 * @param importFileCommand the importFileCommand to set
	 */
	protected void setImportFileCommand(ImportFileProcessorCommand importFileCommand) {
		this.importFileCommand = importFileCommand;
	}

	/**
	 * @param importRowCommand the importRowCommand to set
	 */
	protected void setImportRowCommand(ImportRowProcessorCommand importRowCommand) {
		this.importRowCommand = importRowCommand;
	}
}
